package com.zlc.common.aop;

import com.zlc.common.annotation.LogSaveAnnotation;
import com.zlc.common.common.service.ContextHolder;
import com.zlc.common.core.sys.model.vo.MayanSysLogVo;
import com.zlc.common.core.sys.service.MayanSysLogService;
import com.zlc.common.model.user.LoginParam;
import com.zlc.common.model.user.UserInfo;
import com.zlc.common.util.IpUtils;
import com.zlc.common.util.JsonHelper;
import com.zlc.common.util.PlatUtils;
import com.zlc.common.util.UserHelper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * 日志切面记录
 */
@Aspect
@Order(-1)
@Slf4j
@Component
public class LogSaveAnnotationAop {

    @Autowired
    private MayanSysLogService mayanSysLogService;

    private static final Integer PAYLOAD_INDEX = 500;

    private String getIp(){
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            return IpUtils.getIp(request);
        }catch (Exception e){}
        return "other";
    }

    @Around("@annotation(ds)")
    public Object logSave(ProceedingJoinPoint joinPoint, LogSaveAnnotation ds) throws Throwable {
        long start = System.currentTimeMillis();
        Date nowDate = new Date();
        String url;
        String httpMethod = null;
        Object result = null;
        List<Object> httpReqArgs = new ArrayList<>();
        String account;
        try {
            UserInfo loginAppUser = UserHelper.getNowUser();
            account = loginAppUser.getUser().getUserAccount();
        } catch (Exception e) {
            log.info("保存日志报错：{}",e.getMessage());
            account = "SYSTEM";
            ContextHolder.set("logAccount",account);
        }
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        LogSaveAnnotation logAnnotation = methodSignature.getMethod().getDeclaredAnnotation(LogSaveAnnotation.class);
        Object[] args = joinPoint.getArgs();
        url = methodSignature.getDeclaringTypeName() + "/" + methodSignature.getName();
        String params = null;

        MayanSysLogVo sysLogVo = new MayanSysLogVo();
        sysLogVo.setCreateUser(account);
        sysLogVo.setUpdateUser(account);
        sysLogVo.setCreateTime(nowDate);
        sysLogVo.setUpdateTime(nowDate);
        sysLogVo.setIp(getIp());
        sysLogVo.setTitle(logAnnotation.title());
        sysLogVo.setCatalog(logAnnotation.catalog());
        sysLogVo.setLogType(logAnnotation.logType());

        for (Object object : args) {
            if (object instanceof HttpServletRequest) {
                HttpServletRequest request = (HttpServletRequest)object;
                url = request.getRequestURI();
                httpMethod = request.getMethod();
            } else if (!(object instanceof HttpServletResponse)) {
                if (!(object instanceof MultipartFile)) {
                    httpReqArgs.add(object);
                }

            } else {
                log.info("");
            }
        }

        try {
            if (logAnnotation.printRequestParam()) {
                if(httpReqArgs.size() == 1){
                    params = JsonHelper.toJson(httpReqArgs.get(0));
                }else {
                    params = JsonHelper.toJson(httpReqArgs);
                }
                if(logAnnotation.logType() == 1){
                    LoginParam loginParam = JsonHelper.jsonToObject(params, LoginParam.class);
                    params = loginParam.getAccount();
                }
                log.info("开始请求,  url={} , httpMethod={}, reqData={} ", url, httpMethod, params);
            } else {
                log.info("开始请求,  url={} , httpMethod={}", url, httpMethod);
            }
        } catch (Exception e) {
            log.error("记录参数失败：{}", e.getMessage());
        }

        Boolean isSuccess = Boolean.TRUE;
        String exceptionMessage = null;
        boolean flag = false;
        try {
            flag = true;
            result = joinPoint.proceed();
            flag = false;
        } catch (Exception e) {
            isSuccess = Boolean.FALSE;
            exceptionMessage = e.getMessage();
            log.error("请求报错,  url={} , httpMethod={}, reqData={} ,error ={} ", url, httpMethod, params, e.getMessage());
            throw e;
        } finally {
            if (flag) {
                if (logAnnotation.recordRequestParam()) {

                    sysLogVo.setModule(logAnnotation.module() + ":" + methodSignature.getDeclaringTypeName() + "/"
                            + methodSignature.getName());
                    sysLogVo.setParams(params);
                    sysLogVo.setFlag(isSuccess);
                    sysLogVo.setErrorMessage(exceptionMessage);

                    CompletableFuture.runAsync(() -> {
                        try {
                            log.trace("日志落库开始：{}", sysLogVo);
                            mayanSysLogService.saveVo(sysLogVo);

                            log.trace("开始落库结束：{}", sysLogVo);
                        } catch (Exception e) {
                            log.error("落库失败->{}", e.getMessage());
                        }

                    }, PlatUtils.THREAD_POOL_EXECUTOR);
                }

                String payload = null;
                if (result != null) {
                    payload = JsonHelper.toJson(result);
                    if (payload.length() > PAYLOAD_INDEX) {
                        payload = payload.subSequence(0, PAYLOAD_INDEX) + "...";
                    }
                }

                log.info("请求完成, 耗时={}, resp={}:", System.currentTimeMillis() - start, payload);
            }
        }

        if (logAnnotation.recordRequestParam()) {

            sysLogVo.setModule(logAnnotation.module() + ":" + methodSignature.getDeclaringTypeName() + "/"
                    + methodSignature.getName());
            sysLogVo.setParams(params);
            sysLogVo.setFlag(isSuccess);
            sysLogVo.setErrorMessage(exceptionMessage);
            CompletableFuture.runAsync(() -> {
                try {
                    log.trace("日志落库开始：{}", sysLogVo);
                    if (this.mayanSysLogService != null) {
                        this.mayanSysLogService.saveVo(sysLogVo);
                    }
                    log.trace("开始落库结束：{}", sysLogVo);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("落库失败：{}", e.getMessage());
                }
            },  PlatUtils.THREAD_POOL_EXECUTOR);
        }

        String payload = null;
        if (result != null) {
            payload = JsonHelper.toJson(result);
            if (payload.length() > PAYLOAD_INDEX) {
                payload = payload.subSequence(0, PAYLOAD_INDEX) + "...";
            }
        }

        log.info("请求完成, 耗时={}, resp={}:", System.currentTimeMillis() - start, payload);
        return result;
    }

}
